-- autocraft_engine.lua
-- Reads a config file and schedules crafting tasks in a Refined Storage network.
-- Version with a professional-grade logging system (levels, rotation).
-- To run in background, type "thread.create(function() dofile("/home/autocraft_engine.lua") end):detach()" in lua terminal.

local component = require("component")
local computer = require("computer")
local filesystem = require("filesystem")
local io = require("io")

local rs = component.block_refinedstorage_grid_0
if not rs then
  print("Error: Component 'block_refinedstorage_grid_0' not found.")
  return
end

-- ##### START OF CONFIGURATION #####
local CONFIG_PATH = "/home/item_num.txt"
local LOG_PATH = "/home/autocraft_log.txt"
local TICK_INTERVAL = 10
-- ##### END OF CONFIGURATION #####


-- #####################################################
-- ## LOGGING SYSTEM
-- #####################################################
local LOG_LEVELS = {
  DEBUG = 1, -- 最详细的调试信息
  INFO  = 2, -- 常规信息，如启动、成功等
  WARN  = 3, -- 警告，可能出现问题但未导致错误
  ERROR = 4  -- 脚本执行发生错误
}

-- ####################################################################
-- ##  !!  在这里修改日志等级  !!
-- ##  例如，改为 LOG_LEVELS.INFO 将不再显示 DEBUG 信息
-- ####################################################################
local CURRENT_LOG_LEVEL = LOG_LEVELS.INFO

local MAX_LOG_LINES = 2000

-- 用于从数字反向查找等级名称的辅助表
local LEVEL_NAMES = {}
for name, num in pairs(LOG_LEVELS) do LEVEL_NAMES[num] = name end

-- 核心日志写入函数
local function write_log(level, message)
  -- 只有当消息的等级 >= 当前设定的等级时，才写入日志
  if level < CURRENT_LOG_LEVEL then
    return
  end

  local file, err = io.open(LOG_PATH, "a")
  if not file then
    print("FATAL: Could not open log file for writing: " .. tostring(err))
    return
  end
  
  local level_name = LEVEL_NAMES[level] or "UNKNOWN"
  local formatted_message = string.format("[%s] [%s] %s\n", os.date("%Y-%m-%d %H:%M:%S"), level_name, message)
  
  file:write(formatted_message)
  file:close()
end

-- 便捷的日志调用接口
log = {
  debug = function(message) write_log(LOG_LEVELS.DEBUG, message) end,
  info  = function(message) write_log(LOG_LEVELS.INFO, message) end,
  warn  = function(message) write_log(LOG_LEVELS.WARN, message) end,
  error = function(message) write_log(LOG_LEVELS.ERROR, message) end
}
-- #####################################################


local function trim_log_file()
  pcall(function()
    if not filesystem.exists(LOG_PATH) then return end
    local file, err = io.open(LOG_PATH, "r")
    if not file then return end

    local lines = {}
    for line in file:lines() do table.insert(lines, line) end
    file:close()

    if #lines > MAX_LOG_LINES then
      log.info("Log file exceeds " .. MAX_LOG_LINES .. " lines. Trimming...")
      
      local new_lines = {}
      local start_index = #lines - MAX_LOG_LINES + 1
      for i = start_index, #lines do table.insert(new_lines, lines[i]) end

      local out_file = io.open(LOG_PATH, "w")
      if out_file then
        out_file:write(table.concat(new_lines, "\n"))
        out_file:close()
      end
    end
  end)
end

print("Starting autocraft engine. All subsequent output will be in '" .. LOG_PATH .. "'. Press Ctrl+C to terminate this shell.")
log.info("Autocraft engine started. Current log level: " .. (LEVEL_NAMES[CURRENT_LOG_LEVEL] or "UNKNOWN"))

while true do
  local function run_cycle()
    trim_log_file()
    
    log.debug("-----------------------------------------------------")
    log.debug("Starting new cycle...")

    local f, err = loadfile(CONFIG_PATH)
    if not f then
      log.error("Failed to load config file. Error: " .. tostring(err))
      return
    end
    
    local target_items = f()
    if type(target_items) ~= "table" or #target_items == 0 then
      log.debug("Config file is empty or invalid. Skipping cycle.")
      return
    end
    log.debug("Config loaded. Found " .. #target_items .. " item(s) to check.")

    if not rs.isConnected() then
      log.warn("RS Grid is not connected. Skipping cycle.")
      return
    end
    log.debug("RS Grid is connected.")
    
    local current_tasks = rs.getTasks()
    log.debug("Fetched " .. #current_tasks .. " existing tasks from the network.")

    local tasks_map = {}
    for _, task in ipairs(current_tasks) do
      if task.stack and task.stack.item then
        local key = task.stack.item.name .. ":" .. tostring(task.stack.item.damage or 0)
        tasks_map[key] = (tasks_map[key] or 0) + task.quantity
      end
    end

    for i, entry in ipairs(target_items) do
      log.debug("\n---> Processing item #" .. i .. " from config: " .. (entry.item.label or "N/A"))
      local item = entry.item
      local target_quantity = entry.quantity

      if not (item and target_quantity > 0) then
        -- This is a normal condition, so no log is necessary unless debugging.
      else
        log.debug("Target quantity is " .. target_quantity .. ". Proceeding...")
        local item_query = {name = item.name, damage = item.damage or 0}
        local pattern = rs.getPattern(item_query)
        
        if not pattern then
          log.debug("No crafting pattern found for this item. Cannot craft.")
        else
          log.debug("Found a valid pattern! Checking quantities...")
          local stack_in_network = rs.getItem(item_query)
          local inventory_count = stack_in_network and stack_in_network.size or 0
          local item_key = item.name .. ":" .. tostring(item.damage or 0)
          local task_count = tasks_map[item_key] or 0
          
          log.debug("In network: " .. inventory_count .. " | In active tasks: " .. task_count)
          local needed_quantity = target_quantity - inventory_count - task_count
          log.debug("Calculation: Needed = " .. target_quantity .. " - " .. inventory_count .. " - " .. task_count .. " = " .. needed_quantity)

          if needed_quantity > 0 then
            log.debug("Need to schedule a task for " .. needed_quantity .. " items.")
            local result, reason = rs.scheduleTask(item_query, needed_quantity)
            
            if result then
              log.info("SUCCESS: Schedule request for " .. needed_quantity .. " of " .. (item.label or item.name) .. " was accepted.")
              
              log.debug("Verifying if task was actually created...")
              os.sleep(0.2)
              local tasks_after_schedule = rs.getTasks()
              local task_found = false
              
              for _, task in ipairs(tasks_after_schedule) do
                if task.stack and task.stack.item then
                  local task_item_key = task.stack.item.name .. ":" .. tostring(task.stack.item.damage or 0)
                  if task_item_key == item_key then
                    task_found = true
                    break
                  end
                end
              end

              if task_found then
                log.debug("VERIFIED: Task is now active in the network.")
              else
                log.warn("Task did not appear in the active queue. This is likely due to MISSING MATERIALS.")
              end
            else
              log.error("Failed to schedule task for " .. (item.label or item.name) .. ". Reason: " .. tostring(reason))
            end
            os.sleep(0.5)
          end
        end
      end
    end
  end

  run_cycle()
  log.debug("Cycle finished. Waiting for " .. TICK_INTERVAL .. " seconds...")
  os.sleep(TICK_INTERVAL)
end
